'''
Created on Jul 9, 2011

@author: kafkef
'''

import time

from PyQt4.QtGui import *
from PyQt4.QtCore import SIGNAL, QObject, Qt, QStringList, QSize, QString

from dexen.system.client import db
from dexen.system.client import ui
from dexen.system.client.proxy import JobProxy
from dexen.system.client.ui.dialogs.new_job_dlg import NewJobDialog

svr_proxy = ui.svr_proxy


class ServerInfoWidgetItem(QTreeWidgetItem):
    def __init__(self, svr_info, status):
        assert isinstance(svr_info, db.ServerInfo)
        svr_info.status = status
        self.svr_info = svr_info
        self.status = status
        name = svr_info.get_name()
        start_time = svr_info.get_start_time_str()
        s_list = QStringList() << name << start_time << status
        QTreeWidgetItem.__init__(self, s_list)
        self.jobs = {} # job_id : JobInfoWidgetItem
        if self.is_connected():
            self.setIcon(0, QIcon(":/assets/server_on.png"))
        else:
            self.setIcon(0, QIcon(":/assets/server_off.png"))
    
    def get_svr_info(self):
        return self.svr_info
    
    def get_name(self):
        return self.svr_info.get_name()
    
    def get_status(self):
        return self.status
    
    def get_start_time(self):
        return self.svr_info.get_start_time()
    
    def is_connected(self):
        return self.status == "Connected"

    def update_job_info(self, jobs):
        # jobs: job_id : db.JobInfo
                
        # Remove deleted jobs if any
        for job_id in self.jobs.keys():
            if not jobs.has_key(job_id):
                job_item = self.jobs[job_id]
                self.takeChild(self.indexOfChild(job_item))
                del self.jobs[job_id]
                
        # Update job status if the job already exists
        # Add job if it doesn't exist.
        for job_id, job_info in jobs.items():
            assert isinstance(job_info, db.JobInfo)
            if not self.jobs.has_key(job_id):
                job_item = JobInfoWidgetItem(job_info, self)
                self.addChild(job_item)
                self.jobs[job_id] = job_item
            else:
                job_item = self.jobs[job_id]
                assert isinstance(job_item, JobInfoWidgetItem)
                job_item.set_status(job_info.get_status())

    def set_status(self, status):
        self.status = status;
        self.setText(2, self.status)
        self.svr_info.status = status
        if self.is_connected():
            self.setIcon(0, QIcon(":/assets/server_on.png"))
        else:
            self.setIcon(0, QIcon(":/assets/server_off.png"))
            
    def connect(self):
        self.set_status("Connected")

    def disconnect(self):
        self.set_status("Disconnected")
        for job_item in self.jobs.values():
            assert isinstance(job_item, JobInfoWidgetItem)
            job_item.set_status("Disconnected")


class JobInfoWidgetItem(QTreeWidgetItem):
    def __init__(self, job_info, parent_item):

        assert isinstance(job_info, db.JobInfo)
        assert isinstance(parent_item, ServerInfoWidgetItem)
        self.job_info = job_info
        name = job_info.get_name()
        start_time = job_info.get_start_time_str()
        self.status = ""
                    
        s_list = QStringList() << name << start_time << self.status
        QTreeWidgetItem.__init__(self, s_list)

        # Determine status
        if parent_item.is_connected():
            self.set_status(job_info.get_status())
        else:
            self.set_status("Disconnected")
    
    def is_connected(self):
        return self.status != "Disconnected"
    
    def get_name(self):
        return self.job_info.get_name()
    
    def get_status(self):
        return self.status
    
    def get_start_time(self):
        return self.job_info.get_start_time()

    def get_job_id(self):
        return self.job_info.get_id()

    def set_status(self, status):
        self.status = status
        self.setText(2, QString(status))
        #if not self.is_connected():
        #    self.setIcon(0, QIcon(":/assets/job_off.png"))   
    
    def get_job_info(self):
        return self.job_info
    


class JobListWidget(QTreeWidget):
    def __init__(self, parent=None):
        QTreeWidget.__init__(self, parent)
        
        # Configure the column properties
        self.setColumnCount(3)
        
        # Configure the header
        header_labels = QStringList()
        header_labels << "Name" << "Start Time" << "Status" 
        self.setHeaderLabels(header_labels)
        header = self.header()
        assert isinstance(header, QHeaderView)
        header.setDefaultAlignment(Qt.AlignLeft)
        
        # Configure the signal/slot connections
        QObject.connect(self, SIGNAL("currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)"),
                        self.on_cur_item_changed)
        self.servers = {} # (name, start_time) : ServerInfoWidgetItem
        #self.setIconSize()
    
    def initialize_servers(self, server_info_list):
        for svr_info in server_info_list:
            assert isinstance(svr_info, db.ServerInfo)
            name = svr_info.get_name()
            start_time = svr_info.get_start_time()
            top_item = ServerInfoWidgetItem(svr_info, "Disconnected")
            self.addTopLevelItem(top_item)
            self.servers[(name, start_time)] = top_item 
            top_item.update_job_info(svr_info.get_jobs())

    def _get_top_item(self, svr_info):
        assert isinstance(svr_info, db.ServerInfo)
        name = svr_info.get_name()
        start_time = svr_info.get_start_time()
        return self.servers[(name, start_time)];

    def update_server(self, svr_info):
        assert isinstance(svr_info, db.ServerInfo)
        assert self._is_cur_svr(svr_info)
        name = svr_info.get_name()
        start_time = svr_info.get_start_time()
        top_item = ServerInfoWidgetItem(svr_info, "Connected")
        if not self.servers.has_key((name, start_time)):
            self.insertTopLevelItem(0, top_item)
            self.servers[(name, start_time)] = top_item
        else:
            top_item = self.servers[(name, start_time)]
            if not top_item.is_connected():
                top_item.connect()
        top_item.update_job_info(svr_info.get_jobs())
    
    def _is_cur_svr(self, svr_info):
        assert isinstance(svr_info, db.ServerInfo)
        return svr_info.get_name() == svr_proxy.get_ip() and \
               svr_info.get_start_time() == svr_proxy.get_start_time()

    def on_cur_item_changed(self, current, previous):
        if current == previous: return
        print "current item changed", current
        if isinstance(current, JobInfoWidgetItem):
            assert isinstance(current, JobInfoWidgetItem)
            self.emit(SIGNAL("cur_job_changed"), current.get_job_info())

    def get_cur_job_info(self):
        item = self.currentItem()
        if isinstance(item, JobInfoWidgetItem):
            assert isinstance(item, JobInfoWidgetItem)
            return item.get_job_info()
        return None

    def get_cur_svr_info(self):
        item = self.currentItem()
        if isinstance(item, JobInfoWidgetItem):
            assert isinstance(item, JobInfoWidgetItem)
            job_info = item.get_job_info()
            assert isinstance(job_info, db.JobInfo)
            return job_info.get_svr_info()

        assert isinstance(item, ServerInfoWidgetItem)
        return item.get_svr_info()

    def on_server_disconnected(self, svr_info):
        top_item = self._get_top_item(svr_info)
        assert isinstance(top_item, ServerInfoWidgetItem)
        top_item.disconnect()
 

class JobNavigatorWidget(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        self._create_widgets()

    def _create_widgets(self):
        # Create the widgets
        self._header_label = QLabel("Jobs")
        self.list_widget = JobListWidget()
        self._toolbar = QToolBar()
        
        # Configure the widgets
        self._toolbar.addAction(QIcon(":/assets/add.png"), "Run a new job",
                                self.on_new_job)
        self._toolbar.addAction(QIcon(":/assets/refresh.png"), "Refresh a job", 
                                self.on_refresh)
        self._toolbar.addAction(QIcon(":/assets/stop.png"), "Stop a job",
                                self.on_stop_job)
        self._toolbar.setIconSize(QSize(20,20))
        
        self.list_widget.setIconSize(QSize(16,16))
        
        # Layout the widgets
        layout = QVBoxLayout()
        layout.setMargin(0)
        layout.addWidget(self._toolbar)
        layout.addWidget(self.list_widget)
        self.setLayout(layout)
        
        # Configure the signal/slot connections
        self.connect(self.list_widget, SIGNAL("cur_job_changed"),
                        self.on_cur_job_changed)   
    
    def _refresh_list(self):
        pass

    def on_new_job(self):
        self.new_job_dlg = NewJobDialog(self)
        ret_value = self.new_job_dlg.exec_()
        if ret_value == QDialog.Accepted:
            time.sleep(1)
            self._refresh_list()
    
    def on_stop_job(self):
        job_info = self.list_widget.get_cur_job_info()
        if job_info is not None:
            assert isinstance(job_info, db.JobInfo)
            job_id = job_info.get_id()
            job_proxy = svr_proxy.get_job_proxy(job_id)
            assert isinstance(job_proxy, JobProxy)
            job_proxy.stop()
        # TODO: on_refresh will cause the entire list to be updated.
        # However, we only want to stop the specified job without possibly
        # changing the contents of the list since the on_refresh might change
        # contents of the list.
        #self.on_refresh()
        
    def on_refresh(self):
        print 'JobNavigator:on_refresh'
        job_info = self.list_widget.get_cur_job_info()
        if job_info is not None:
            self.emit(SIGNAL("refreshed"), job_info)
            
    def on_cur_job_changed(self, job_info):
        ui.set_job_info(job_info)
        self.emit(SIGNAL("cur_job_changed"), job_info)
    
    def on_server_info_initialized(self, svr_info_list):
        print "JobNavigator:on_server_info_initialized"
        self.list_widget.initialize_servers(svr_info_list)    
    
    def on_server_info_updated(self, svr_info):
        print "JobNavigator:on_server_info_updated"
        self.list_widget.update_server(svr_info)

    def on_server_disconnected(self, svr_info):
        self.list_widget.on_server_disconnected(svr_info)
    
